/*
 * @author Stanislovas Mickus
 * @version 1
 * @date 18 December, 2014
 *
 *  Copyright (C) 2013 Stanislovas Mickus
 *
 * Licensed under the GNU General Public License, Version 3 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.gnu.org/copyleft/gpl.html
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.stasmobstudios.musicplayer.fragments;

import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.media.audiofx.BassBoost;
import android.media.audiofx.Equalizer;
import android.media.audiofx.Virtualizer;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.SeekBar;
import android.widget.Spinner;
import android.widget.TextView;

import com.stasmobstudios.musicplayer.R;
import com.stasmobstudios.musicplayer.activities.MusicPlayerUI;
import com.stasmobstudios.musicplayer.adapters.PresetAdapter;
import com.stasmobstudios.musicplayer.items.Preset;
import com.stasmobstudios.musicplayer.util.ConstantsGUIUpdate;
import com.stasmobstudios.musicplayer.views.RotaryKnobView;
import com.stasmobstudios.musicplayer.views.VerticalSeekBar;

import java.util.ArrayList;
import java.util.List;

public class EQFragment extends Fragment{
    private final String TAG = "EQFragment";
    private View eqView;
    private MusicPlayerUI mMusicPlayerUI;
    protected Equalizer presetEQ;
    protected BassBoost bassBoost;
    protected Virtualizer virtualizerEffect;
    private int [] presetBands = {R.id.eq1, R.id.eq2, R.id.eq3, R.id.eq4, R.id.eq5, R.id.eq6, R.id.eq7, R.id.eq8};
    private int mPrevSelectedPreset = 3, CUSTOM_PRESET_ID = -1;

    public EQFragment() {
        super();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        this.mMusicPlayerUI = MusicPlayerUI.getMusicPlayerActivity();
        eqView = inflater.inflate(R.layout.music_eq_view, container, false);

        return eqView;
    }

    /**
     * Initialize EQ fragment view
     */
    public void initEQView() {
        int audioSessionID = mMusicPlayerUI.getPlaybackSrevice().getAudioSessionID();
        presetEQ = new Equalizer(0, audioSessionID);

        //Preset initalization
        final Spinner presetView = (Spinner) eqView.findViewById(R.id.preset_spinner);

        final List<Preset> presetList = new ArrayList<Preset>();
        // Get proper band background and calculate distance from each band
        int nrPrestBands = presetEQ.getNumberOfBands();
        Drawable progressDrawable = getResources().getDrawable(R.drawable.eq_bg);
        SeekBar eqBandTmpl = (SeekBar) eqView.findViewById(presetBands[0]);
        progressDrawable.setBounds(0, 0, eqBandTmpl.getWidth(), eqBandTmpl.getHeight());
        int paddingEQBandsHorizontal = (eqView.findViewById(R.id.eqPlaceHolder).getWidth() - eqBandTmpl.getWidth() * nrPrestBands) / (2 * nrPrestBands);
        int eqHeadPadding = getResources().getDrawable(R.drawable.eq_head).getIntrinsicHeight() / 2;
        for (int i = 0; i < nrPrestBands && i < presetBands.length; i++) {
            SeekBar eqBand = (SeekBar) eqView.findViewById(presetBands[i]);
            eqBand.setVisibility(View.VISIBLE);
            eqBand.setOnSeekBarChangeListener(mEQBandListener);
            // Set proper drawable and paddings
            eqBand.setProgressDrawable(progressDrawable);
            eqBand.setPadding(eqHeadPadding, paddingEQBandsHorizontal, eqHeadPadding, paddingEQBandsHorizontal);
        }

        try {
            //Enable bass boost effect
            TextView bassLabel = (TextView) eqView.findViewById(R.id.bassLabel);
            int bassLevel = Integer.parseInt(bassLabel.getText().toString());
            bassBoost = new BassBoost(100, audioSessionID);
            bassBoost.setStrength((short) (bassLevel * 10));
            bassBoost.setEnabled(true);

            //Enable virtualizer effect
            TextView virtualizerLabel = (TextView) eqView.findViewById(R.id.virtualizerLabel);
            int virtualizerLevel = Integer.parseInt( virtualizerLabel.getText().toString());
            virtualizerEffect = new Virtualizer(100, audioSessionID);
            virtualizerEffect.setStrength((short) (virtualizerLevel * 10));
            virtualizerEffect.setEnabled(true);

            mMusicPlayerUI.getPlaybackSrevice().setAuxEffectSendLevel((float) 1.0);

            Log.d(TAG, "Bass boost " + bassBoost.hasControl());
            Log.d(TAG, "Virtualizer boost " + virtualizerEffect.hasControl());

            //Initilize presets
            for (short i = 0; presetEQ.getNumberOfPresets() > i; i++) {
                String presetName = presetEQ.getPresetName(i);
                Preset preset = new Preset(presetName, i);
                presetList.add(preset);
                Log.d(TAG, "Preset " + i + ": " + presetName);
            }
            // Add preset for custom bands
            presetList.add(new Preset("Custom", CUSTOM_PRESET_ID));

            if (!presetEQ.getEnabled()) {
                presetEQ.setEnabled(true);
            }
            presetEQ.usePreset((short) 3);
            refreshEQBandsSliders();

            Log.d(TAG, "End init EQ.");
        } catch(Exception e) {
            Log.d(TAG, "InitEQView(): FAILED to initilize audio engine effects");
            e.printStackTrace();
        }

        PresetAdapter presetAdapter = new PresetAdapter(mMusicPlayerUI.getBaseContext(), presetList);
        presetView.setAdapter(presetAdapter);
        ((Spinner) eqView.findViewById(R.id.preset_spinner)).setOnItemSelectedListener(mEQItemSelectionListener);

        //Volume controller
        AudioManager audioManager = (AudioManager) mMusicPlayerUI.getSystemService(Context.AUDIO_SERVICE);
        TextView volLabel = (TextView) eqView.findViewById(R.id.volLabel);
        RotaryKnobView volKnobView = (RotaryKnobView) eqView.findViewById(R.id.volumeKnob);
        volKnobView.setStopSwipeViewPager(mMusicPlayerUI.getmMusicPlayerUIViewPager());
        int currentVol = (audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) * 100) / audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
        volLabel.setText(String.valueOf(currentVol));
        volKnobView.setValue(currentVol);
        volKnobView.setKnobListener(new RotaryKnobView.RotaryKnobListener() {
            @Override
            public void onKnobChanged(int arg) {
                TextView volLabel = (TextView) eqView.findViewById(R.id.volLabel);
                int newVolLevel = Integer.parseInt(volLabel.getText().toString()) + arg;
                AudioManager audioManager = (AudioManager) mMusicPlayerUI.getBaseContext().getSystemService(Context.AUDIO_SERVICE);
                int streamVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC) * newVolLevel / 100;
                audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, streamVolume, 0);
                volLabel.setText(String.valueOf(newVolLevel));
            }

        });

        //Bass controller
        RotaryKnobView bassKnobView = (RotaryKnobView) eqView.findViewById(R.id.bassKnob);
        bassKnobView.setStopSwipeViewPager(mMusicPlayerUI.getmMusicPlayerUIViewPager());
        bassKnobView.setKnobListener(new RotaryKnobView.RotaryKnobListener() {
            @Override
            public void onKnobChanged(int arg) {
                TextView bassLabel = (TextView) eqView.findViewById(R.id.bassLabel);
                int bassLevel = Integer.parseInt(bassLabel.getText().toString()) + arg;
                bassLabel.setText(String.valueOf(bassLevel));
                if (EQFragment.this.bassBoost != null)
                    EQFragment.this.bassBoost.setStrength((short) (bassLevel * 10));
            }

        });

        //Treble controller
        RotaryKnobView virtualizerKnobView = (RotaryKnobView) eqView.findViewById(R.id.virtualizerKnob);
        virtualizerKnobView.setStopSwipeViewPager(mMusicPlayerUI.getmMusicPlayerUIViewPager());
        virtualizerKnobView.setKnobListener(new RotaryKnobView.RotaryKnobListener() {
            @Override
            public void onKnobChanged(int arg) {
                TextView trebleLabel = (TextView) eqView.findViewById(R.id.virtualizerLabel);
                int virtualizerLevel = Integer.parseInt(trebleLabel.getText().toString()) + arg;
                trebleLabel.setText(String.valueOf(virtualizerLevel));

                if (EQFragment.this.virtualizerEffect != null)
                    EQFragment.this.virtualizerEffect.setStrength((short) (virtualizerLevel * 10));
            }
        });
    }

    /**
     * Listener for EQ handling events
     */
    private SeekBar.OnSeekBarChangeListener mEQBandListener = new SeekBar.OnSeekBarChangeListener() {
        @Override
        public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
            // Set to custom preset and update preset bands from sliders
            Spinner eqSpinner = (Spinner) eqView.findViewById(R.id.preset_spinner);
            int custom = eqSpinner.getAdapter().getCount() - 1;

            if (mPrevSelectedPreset != custom) {
                // Avoid restoring all preset bands when setting custom preset on EQ band slide
                ((Preset) eqSpinner.getItemAtPosition(custom)).setRestoreSlidesState(false);
                eqSpinner.setSelection(custom);
            }
            updateEQFromSliders();
        }

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {}

        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {}
    };

    private AdapterView.OnItemSelectedListener mEQItemSelectionListener = new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
            if (adapterView.getCount() <= i)
                return;

            // Restore preset for custom equalizer when preset change not initiated from EQ band slide
            // Save custom preset if leaving it and set selected preset
            int custom = adapterView.getAdapter().getCount() - 1;

            if (((Preset) adapterView.getItemAtPosition(i)).getPresetID() == CUSTOM_PRESET_ID && ((Preset) adapterView.getItemAtPosition(i)).isRestoreSlidesState()) {
                restoreState(true);
            } else if (((Preset) adapterView.getItemAtPosition(mPrevSelectedPreset)).getPresetID() == CUSTOM_PRESET_ID) {
                saveState(true);
                setPreset((short) i);
            } else if (custom != i)
                setPreset((short) i);

            if (!((Preset) adapterView.getItemAtPosition(i)).isRestoreSlidesState())
                ((Preset) adapterView.getItemAtPosition(i)).setRestoreSlidesState(true);

            mPrevSelectedPreset = i;
        }

        @Override
        public void onNothingSelected(AdapterView<?> adapterView) {
        }
    };

    /**
     **************************  EQ handling methods   **************************
     */

    private void enableEQBandListeners(boolean enableListeners) {
        for (int i = 0; i < presetEQ.getNumberOfBands(); i++) {
            SeekBar eqBand = (SeekBar) eqView.findViewById(presetBands[i]);
            if (enableListeners)
                eqBand.setOnSeekBarChangeListener(mEQBandListener);
            else
                eqBand.setOnSeekBarChangeListener(null);
        }
    }

    // Update equalizer from slider positions
    protected void updateEQFromSliders() {
        short scaleBottom = 0;
        if (presetEQ.getBandLevelRange()[0] < 0) scaleBottom = presetEQ.getBandLevelRange()[0];
        int bandLength = presetEQ.getBandLevelRange()[1] - presetEQ.getBandLevelRange()[0];

        for (short i = 0; i < presetEQ.getNumberOfBands(); i++) {
            short level = (short) ((bandLength * ((SeekBar) eqView.findViewById(presetBands[i])).getProgress()) / 100 + scaleBottom);
            presetEQ.setBandLevel(i, level);
        }
    }

    // Set EQ band sliders from current EQ
    public void refreshEQBandsSliders() {
        enableEQBandListeners(false);

        int scaleBottom = 0;
        float bandLevelLength = (float)(presetEQ.getBandLevelRange()[1] - presetEQ.getBandLevelRange()[0]);
        if (presetEQ.getBandLevelRange()[0] < 0) scaleBottom = presetEQ.getBandLevelRange()[0];

        for (short i = 0; i < presetEQ.getNumberOfBands(); i++) {
            int level = (int) ((presetEQ.getBandLevel(i) - scaleBottom) * 100 / bandLevelLength);
            Log.d("APP", "EQ % set " + i + ", level: " + level);
            ((SeekBar) eqView.findViewById(presetBands[i])).setProgress(level);
        }

        enableEQBandListeners(true);
    }

    // Set EQ and update its bands
    public void setPreset(int presetID) {
        presetEQ.usePreset((short) presetID);
        refreshEQBandsSliders();
        if (!presetEQ.getEnabled()) {
            presetEQ.setEnabled(true);
        }
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        //Initialize EQ after connected to playback service
        mMusicPlayerUI.setOnConectedToPlaybackServiceListener(new MusicPlayerUI.OnConnectedToPlaybackServiceListener() {
            @Override
            public void onConnected() {
                initEQView();
            }
        });
    }


    // Add library info to prefs
    private boolean saveState(boolean eqBandsOnly) {
        SharedPreferences trackViewInfo = getActivity().getSharedPreferences(ConstantsGUIUpdate.BUNDLE_UPDATE_EQ_FRAGMENT, Context.MODE_PRIVATE);
        SharedPreferences.Editor edit = trackViewInfo.edit();

        if (!eqBandsOnly) {
            edit.putInt(ConstantsGUIUpdate.BUNDLE_UPDATE_EQ_FRAGMENT_VOL, Integer.valueOf(((TextView) eqView.findViewById(R.id.volLabel)).getText().toString()));
            edit.putInt(ConstantsGUIUpdate.BUNDLE_UPDATE_EQ_FRAGMENT_BASS, Integer.valueOf(((TextView) eqView.findViewById(R.id.bassLabel)).getText().toString()));
            edit.putInt(ConstantsGUIUpdate.BUNDLE_UPDATE_EQ_FRAGMENT_VIRT, Integer.valueOf(((TextView) eqView.findViewById(R.id.virtualizerLabel)).getText().toString()));
        }
        short eqID = presetEQ.getCurrentPreset();
        edit.putInt(ConstantsGUIUpdate.BUNDLE_UPDATE_EQ_FRAGMENT_PRESET, eqID);
        if (eqID == CUSTOM_PRESET_ID) {
            edit.putInt(ConstantsGUIUpdate.BUNDLE_UPDATE_EQ_FRAGMENT_PRESET, -1);
            edit.putInt(ConstantsGUIUpdate.BUNDLE_UPDATE_EQ_FRAGMENT_EQ1, ((VerticalSeekBar) eqView.findViewById(R.id.eq1)).getProgress());
            edit.putInt(ConstantsGUIUpdate.BUNDLE_UPDATE_EQ_FRAGMENT_EQ2, ((VerticalSeekBar) eqView.findViewById(R.id.eq2)).getProgress());
            edit.putInt(ConstantsGUIUpdate.BUNDLE_UPDATE_EQ_FRAGMENT_EQ3, ((VerticalSeekBar) eqView.findViewById(R.id.eq3)).getProgress());
            edit.putInt(ConstantsGUIUpdate.BUNDLE_UPDATE_EQ_FRAGMENT_EQ4, ((VerticalSeekBar) eqView.findViewById(R.id.eq4)).getProgress());
            edit.putInt(ConstantsGUIUpdate.BUNDLE_UPDATE_EQ_FRAGMENT_EQ5, ((VerticalSeekBar) eqView.findViewById(R.id.eq5)).getProgress());
        }
        edit.putString(ConstantsGUIUpdate.BUNDLE_UPDATE_EQ_FRAGMENT_DATA, "DADA IN");
        edit.commit();

        return true;
    }

    //Updating music library view
    private boolean restoreState(final boolean eqBandsOnly) {
        SharedPreferences eqViewInfo = getActivity().getSharedPreferences(ConstantsGUIUpdate.BUNDLE_UPDATE_EQ_FRAGMENT, Context.MODE_PRIVATE);
        if (eqViewInfo != null && eqViewInfo.contains(ConstantsGUIUpdate.BUNDLE_UPDATE_EQ_FRAGMENT_DATA)) {
            // Restore equlizer state after presetEQ initialization
            (new Thread(new Runnable() {
                @Override
                public void run() {
                    boolean run = true;
                    // Restore only when preset equalizer was connected
                    while (run) {
                        if (presetEQ == null) {
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            continue;
                        }

                        run = false;
                        mMusicPlayerUI.runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                SharedPreferences eqViewInfo = getActivity().getSharedPreferences(ConstantsGUIUpdate.BUNDLE_UPDATE_EQ_FRAGMENT, Context.MODE_PRIVATE);
                                if (!eqBandsOnly) {
                                    ((TextView) eqView.findViewById(R.id.volLabel)).setText(String.valueOf(eqViewInfo.getInt(ConstantsGUIUpdate.BUNDLE_UPDATE_EQ_FRAGMENT_VOL, 66)));
                                    ((TextView) eqView.findViewById(R.id.bassLabel)).setText(String.valueOf(eqViewInfo.getInt(ConstantsGUIUpdate.BUNDLE_UPDATE_EQ_FRAGMENT_BASS, 66)));
                                    ((TextView) eqView.findViewById(R.id.virtualizerLabel)).setText(String.valueOf(eqViewInfo.getInt(ConstantsGUIUpdate.BUNDLE_UPDATE_EQ_FRAGMENT_VIRT, 66)));
                                    ((RotaryKnobView) eqView.findViewById(R.id.volumeKnob)).setValue(eqViewInfo.getInt(ConstantsGUIUpdate.BUNDLE_UPDATE_EQ_FRAGMENT_VOL, 66));
                                    ((RotaryKnobView) eqView.findViewById(R.id.bassKnob)).setValue(eqViewInfo.getInt(ConstantsGUIUpdate.BUNDLE_UPDATE_EQ_FRAGMENT_BASS, 66));
                                    ((RotaryKnobView) eqView.findViewById(R.id.virtualizerKnob)).setValue(eqViewInfo.getInt(ConstantsGUIUpdate.BUNDLE_UPDATE_EQ_FRAGMENT_VIRT, 66));
                                }

                                short eqID = (short) eqViewInfo.getInt(ConstantsGUIUpdate.BUNDLE_UPDATE_EQ_FRAGMENT_PRESET, 0);
                                Spinner presetSpinner = ((Spinner) eqView.findViewById(R.id.preset_spinner));
                                if (eqID != CUSTOM_PRESET_ID) {
                                    presetSpinner.setSelection(eqID);
                                } else {
                                    enableEQBandListeners(false);
                                    ((VerticalSeekBar) eqView.findViewById(R.id.eq1)).setProgress(eqViewInfo.getInt(ConstantsGUIUpdate.BUNDLE_UPDATE_EQ_FRAGMENT_EQ1, 50));
                                    ((VerticalSeekBar) eqView.findViewById(R.id.eq2)).setProgress(eqViewInfo.getInt(ConstantsGUIUpdate.BUNDLE_UPDATE_EQ_FRAGMENT_EQ2, 50));
                                    ((VerticalSeekBar) eqView.findViewById(R.id.eq3)).setProgress(eqViewInfo.getInt(ConstantsGUIUpdate.BUNDLE_UPDATE_EQ_FRAGMENT_EQ3, 50));
                                    ((VerticalSeekBar) eqView.findViewById(R.id.eq4)).setProgress(eqViewInfo.getInt(ConstantsGUIUpdate.BUNDLE_UPDATE_EQ_FRAGMENT_EQ4, 50));
                                    ((VerticalSeekBar) eqView.findViewById(R.id.eq5)).setProgress(eqViewInfo.getInt(ConstantsGUIUpdate.BUNDLE_UPDATE_EQ_FRAGMENT_EQ5, 50));
                                    enableEQBandListeners(true);
                                    eqID = (short) (presetSpinner.getAdapter().getCount() - 1);
                                    ((Spinner) eqView.findViewById(R.id.preset_spinner)).setSelection(eqID);
                                    updateEQFromSliders();
                                }
                            }
                        });
                    }
                }
            })).start();
        }
        return true;
    }

    @Override
    public void onStop() {
        saveState(false);

        super.onStop();
    }

    @Override
    public void onPause() {
        saveState(false);

        super.onPause();
    }

    @Override
    public void onStart() {
        super.onStart();

        restoreState(false);
    }
}
